﻿Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Transactions

Namespace System.Workflow.Runtime
    Friend NotInheritable Class WorkBatch
        Implements IWorkBatch, IDisposable
        ' Methods
        Private Sub New()
            Me.mutex = New Object
        End Sub

        Friend Sub New(ByVal workBackCollection As WorkBatchCollection)
            Me.mutex = New Object
            Me._pendingWorkCollection = New PendingWorkCollection
            Me._state = WorkBatchState.Usable
            Me._collection = workBackCollection
        End Sub

        Friend Sub Commit(ByVal transaction As Transaction)
            SyncLock Me.mutex
                Me._pendingWorkCollection.Commit(transaction)
            End SyncLock
        End Sub

        Friend Sub Complete(ByVal succeeded As Boolean)
            SyncLock Me.mutex
                If Me._pendingWorkCollection.IsUsable Then
                    Me._pendingWorkCollection.Complete(succeeded)
                    Me._pendingWorkCollection.Dispose
                    Me._state = WorkBatchState.Completed
                End If
            End SyncLock
        End Sub

        Public Sub Dispose()
            Me.Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub

        Private Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                Me._pendingWorkCollection.Dispose
                Me._pendingWorkCollection = Nothing
            End If
        End Sub

        Friend Sub Merge(ByVal batch As WorkBatch)
            If (Not batch Is Nothing) Then
                If (Me._pendingWorkCollection Is Nothing) Then
                    Throw New ObjectDisposedException("WorkBatch")
                End If
                SyncLock Me.mutex
                    SyncLock batch.mutex
                        Dim pair As KeyValuePair([Of] IPendingWork, SortedList([Of] Long, Object))
                        For Each pair In batch._pendingWorkCollection.WorkItems
                            Dim pair2 As KeyValuePair([Of] Long, Object)
                            For Each pair2 In pair.Value
                                Me._pendingWorkCollection.Add(pair.Key, pair2.Key, pair2.Value)
                            Next
                        Next
                    End SyncLock
                    Me._state = WorkBatchState.Merged
                End SyncLock
            End If
        End Sub

        Friend Sub SetWorkBatchCollection(ByVal workBatchCollection As WorkBatchCollection)
            Me._collection = workBatchCollection
        End Sub

        Private Sub System.Workflow.Runtime.IWorkBatch.Add(ByVal work As IPendingWork, ByVal workItem As Object) Implements IWorkBatch.Add
            If (Me._pendingWorkCollection Is Nothing) Then
                Throw New ObjectDisposedException("WorkBatch")
            End If
            SyncLock Me.mutex
                Me._pendingWorkCollection.Add(work, Me._collection.GetNextWorkItemOrderId(work), workItem)
            End SyncLock
        End Sub


        ' Properties
        Friend ReadOnly Property Count As Integer
            Get
                Return Me._pendingWorkCollection.WorkItems.Count
            End Get
        End Property

        Friend ReadOnly Property IsDirty As Boolean
            Get
                Return Me._pendingWorkCollection.IsDirty
            End Get
        End Property


        ' Fields
        Private _collection As WorkBatchCollection
        Private _pendingWorkCollection As PendingWorkCollection
        Private _state As WorkBatchState
        Private mutex As Object

        ' Nested Types
        Friend NotInheritable Class PendingWorkCollection
            Implements IDisposable
            ' Methods
            Friend Sub New()
            End Sub

            Friend Sub Add(ByVal work As IPendingWork, ByVal orderId As Long, ByVal workItem As Object)
                Dim list As SortedList([Of] Long, Object) = Nothing
                If Not Me.Items.TryGetValue(work, list) Then
                    list = New SortedList([Of] Long, Object)
                    Me.Items.Add(work, list)
                End If
                list.Add(orderId, workItem)
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "pending work hc {0} added workItem hc {1}", New Object() { work.GetHashCode, workItem.GetHashCode })
            End Sub

            Friend Sub Commit(ByVal transaction As Transaction)
                Dim pair As KeyValuePair([Of] IPendingWork, SortedList([Of] Long, Object))
                For Each pair In Me.Items
                    Dim key As IPendingWork = pair.Key
                    Dim items As New List([Of] Object)(pair.Value.Values)
                    key.Commit(transaction, items)
                Next
            End Sub

            Friend Sub Complete(ByVal succeeded As Boolean)
                Dim pair As KeyValuePair([Of] IPendingWork, SortedList([Of] Long, Object))
                For Each pair In Me.Items
                    Dim key As IPendingWork = pair.Key
                    Dim items As New List([Of] Object)(pair.Value.Values)
                    Try 
                        key.Complete(succeeded, items)
                        [Continue] For
                    Catch exception As Exception
                        If WorkflowExecutor.IsIrrecoverableException(exception) Then
                            Throw
                        End If
                        WorkflowTrace.Runtime.TraceEvent(TraceEventType.Warning, 0, "Work Item {0} threw exception on complete notification", New Object() { pair.GetType })
                        [Continue] For
                    End Try
                Next
            End Sub

            Public Sub Dispose()
                Me.Dispose(True)
                GC.SuppressFinalize(Me)
            End Sub

            Private Sub Dispose(ByVal disposing As Boolean)
                If (disposing AndAlso (Not Me.Items Is Nothing)) Then
                    Me.Items.Clear
                    Me.Items = Nothing
                End If
            End Sub


            ' Properties
            Friend ReadOnly Property IsDirty As Boolean
                Get
                    If Me.IsUsable Then
                        Dim pair As KeyValuePair([Of] IPendingWork, SortedList([Of] Long, Object))
                        For Each pair In Me.WorkItems
                            Try 
                                If pair.Key.MustCommit(pair.Value) Then
                                    Return True
                                End If
                                [Continue] For
                            Catch exception As Exception
                                If WorkflowExecutor.IsIrrecoverableException(exception) Then
                                    Throw
                                End If
                                [Continue] For
                            End Try
                        Next
                    End If
                    Return False
                End Get
            End Property

            Friend ReadOnly Property IsUsable As Boolean
                Get
                    Return (Not Me.Items Is Nothing)
                End Get
            End Property

            Friend ReadOnly Property WorkItems As Dictionary([Of] IPendingWork, SortedList([Of] Long, Object))
                Get
                    Return Me.Items
                End Get
            End Property


            ' Fields
            Private Items As Dictionary([Of] IPendingWork, SortedList([Of] Long, Object)) = New Dictionary([Of] IPendingWork, SortedList([Of] Long, Object))
        End Class
    End Class
End Namespace

